package com.tau.egonetwork.gui.core.graphs;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Paint;
import java.util.ArrayList;
import java.util.Map;
import java.util.TreeMap;

import org.apache.commons.collections15.Transformer;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.TabItem;

import com.tau.egonetwork.algorithms.GraphAdapter;
import com.tau.egonetwork.profiles.Group;
import com.tau.egonetwork.profiles.MainProfile;
import com.tau.egonetwork.profiles.Profile;

import edu.uci.ics.jung.algorithms.layout.Layout;
import edu.uci.ics.jung.algorithms.layout.StaticLayout;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.graph.UndirectedSparseGraph;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import edu.uci.ics.jung.visualization.layout.LayoutTransition;
import edu.uci.ics.jung.visualization.util.Animator;

public class GroupsVisualizeTab<T extends Comparable<T>> extends BasicVisualizeTab<T> {

	private static final Color EGO_COLOR = Color.RED;
	private static final Color[] CYCLE_COLORS =  { 	Color.GREEN,
													new Color(0, 128, 192),
													Color.ORANGE ,
													Color.MAGENTA };
	
	private static final int BASIC_RADIUS = 50;
	
	private static final int LAYOUT_BIRDVIEW = 0;
	private static final int LAYOUT_PLAINVIEW = 1;
	
	private int highestPriority = 1;
	
	private Map<T, Integer> relevantNodes;
	
	private int currentLayout;
	private StaticLayout<T, Integer> birdViewLayout;
	private StaticLayout<T, Integer> plainViewLayout;
	
	public GroupsVisualizeTab(TabItem tab, Display display) {
		super(tab, display, true);
	}
	
	@Override
	public Graph<T, Integer> buildGraph(MainProfile<T> profile) {

		if (profile.getGroupsList() == null || profile.getGroupsList().getPriority(1) == null)
			return new UndirectedSparseGraph<T, Integer>();

		originalGraph = GraphAdapter.getGraph(profile);
		
		Graph<T, Integer> graph = GraphAdapter.getGraph(profile);
		
		//build a tree of all relevant nodes (that should be visualized):
		relevantNodes = new TreeMap<T, Integer>();
		for (int i = 1; i <= 4; i++) {
			
			Group<T> group = profile.getGroupsList().getPriority(i);
			if (group == null)
				break;
			
			for (Profile<T> p : group) {
				relevantNodes.put(p.getID(), i);
			}
			
		}
		
		//go over all graph nodes and discard the irrelevant ones:
		ArrayList<T> irrelevantNodes = new ArrayList<T>();
		for (T t : graph.getVertices()) {
			if (!relevantNodes.containsKey(t)) {
				if (!t.equals(profile.getID())) { //don't remove egocenteric vertex
					irrelevantNodes.add(t);
				}
			}
		}
		
		for (T t : irrelevantNodes)
			graph.removeVertex(t);
		
		return graph;
	}
	
	public StaticLayout<T, Integer> buildBirdViewLayout(MainProfile<T> profile, Graph<T, Integer> graph) {
		
		StaticLayout<T, Integer> layout = new StaticLayout<T, Integer>(graph);
		layout.setSize(new Dimension(WIDTH - 50, HEIGHT - 50));
		
		int centerX = WIDTH/2;
		int centerY = HEIGHT/2;

		//place egocenteric in center:
		layout.setLocation(profile.getID(), centerX, centerY);
						
		if (profile.getGroupsList() != null) {
			
			for (int i = 1; i <= 4; i++) {
				
				int radius = BASIC_RADIUS * i * i;
				
				Group<T> group = profile.getGroupsList().getPriority(i);
				if (group == null)
					break;
				
				highestPriority = i;

				int count = group.size();
				double increment = 360.0 / count;
				//add all profiles in group to graph:
				for (int j = 0; j < count; j++) {
					layout.setLocation(group.get(j).getID(),
							centerX + radius*Math.sin(j * increment * Math.PI / 180.0),
							centerY + radius*Math.cos(j * increment * Math.PI / 180.0));
				}
								
			}
			
		}

		return layout;
		
	}

	public StaticLayout<T, Integer> buildPlainViewLayout(MainProfile<T> profile, Graph<T, Integer> graph) {
		
		StaticLayout<T, Integer> layout = new StaticLayout<T, Integer>(graph);
		layout.setSize(new Dimension(WIDTH - 50, HEIGHT - 50));
		
		int centerX = WIDTH/2;
		int basicY = 15;
						
		if (profile.getGroupsList() != null) {
			
			for (int i = 1; i <= 4; i++) {
				
				int radius = BASIC_RADIUS * i * i;
				
				Group<T> group = profile.getGroupsList().getPriority(i);
				if (group == null)
					break;
				
				highestPriority = i;

				int count = group.size();
				double increment = 360.0 / count;
				//add all profiles in group to graph:
				for (int j = 0; j < count; j++) {
					layout.setLocation(group.get(j).getID(),
							centerX + radius * Math.sin(Math.PI * j * increment / 180.0),
							//we want to place lowest priority on top, so we calculate i=1 as greater y than i=2 and so on
							basicY + (5 - i) * 75  + j % 15);
				}
								
			}
			
		}
		
		//place egocenteric on bottom:
		layout.setLocation(profile.getID(), centerX, basicY + 5*75);

		return layout;
		
	}
	
	@Override
	protected void populateViewCombo(Combo viewCombo) {
		viewCombo.add("Bird View ");
		viewCombo.add("Plain View ");
		viewCombo.select(0);
	}
	
	@Override
	protected Listener getViewComboSelectionListener(final VisualizationViewer<T, Integer> viewer) {
		
		return new Listener() {
			
			@Override
			public void handleEvent(Event e) {
				
				int selectedIndex = ((Combo)e.widget).getSelectionIndex();
				if (currentLayout == LAYOUT_BIRDVIEW) {
					if (selectedIndex == LAYOUT_PLAINVIEW) {
						currentLayout = LAYOUT_PLAINVIEW;
			    		LayoutTransition<T, Integer> lt =
			    				new LayoutTransition<T, Integer>(viewer,
			    						birdViewLayout, plainViewLayout);
			    		Animator animator = new Animator(lt);
			    		animator.start();
			    		viewer.repaint();
					}
				} else { //currentLayout = LAYOUT_PLAINVIEW
					if (selectedIndex == LAYOUT_BIRDVIEW) {
						currentLayout = LAYOUT_BIRDVIEW;
			    		LayoutTransition<T, Integer> lt =
			    				new LayoutTransition<T, Integer>(viewer,
			    						plainViewLayout, birdViewLayout);
			    		Animator animator = new Animator(lt);
			    		animator.start();
			    		viewer.repaint();						
					}
				}
				
			}
		};
		
	}

	@Override
	protected Layout<T, Integer> getLayout(MainProfile<T> profile, Graph<T, Integer> graph) {
		birdViewLayout = buildBirdViewLayout(profile, graph);
		plainViewLayout = buildPlainViewLayout(profile, graph);
		return birdViewLayout;
	}

	@Override
	protected Transformer<T, Paint> getVertexFillColorTransformer(final MainProfile<T> profile) {

		return new Transformer<T, Paint>() {

			@Override
			public Paint transform(T vertex) {
								
				Color color = null;
				if (vertex.equals(profile.getID())) {
					color = EGO_COLOR;
				} else {
					color = CYCLE_COLORS[relevantNodes.get(vertex) - 1];					
				}
				
				if (currentCluster == null) {
					//not showing clusters, return colors as is:
					return color;
				}
				
				//showing clusters. If vertex not in cluster, made it faded:
				if (currentCluster.get(vertex) == null) {
					return new Color(color.getRed(), color.getGreen(), color.getBlue(), 100);
				}
				
				//otherwise, in cluster, return as normal:
				return color;
			}
			
		};
		
	}

	@Override
	protected String getGroupName(MainProfile<T> profile, T id) {
		return profile.getGroupsList().getPriority(relevantNodes.get(id)).getGroupName();
	}

	@Override
	protected Float getHowMuchToScale() {
		return (1 / (1f + 0.5f*highestPriority));
	}
	
}
